#ifndef UNIT_H
#define UNIT_H

#include "charset.h"
#include "unitclass.h"
#include "unittype.h"
#include "team.h"

class GameMap;

struct UnitProperties
{

    QString name;
    QString charsetPath;
    Charset spriteRef;
    //QString faceSpritePath;
    //SpriteRef faceSprite;

    UnitClass unitClass;
    UnitType unitType;

    qint32 priority;    //an unit with a high priority will play its turn sooner

    qint32 level;
    qint32 exp;

    qint32 maxHealth;
    qint32 health;
    qint32 maxMagic;
    qint32 magic;
    quint8 movement;
    qint32 speed;
    qint32 strength;
    qint32 defense;
    qint32 agility;
    qint32 intelligence;

    QBitArray state;
    QBitArray resistance;
    QBitArray weakness;

    //QList<Spell*> spells
    //Weapon* m_weapon; //equipped weapon
    //Armor* m_armor;   //equipped armor
    //QList<Item*> m_items;
};

enum PredefinedUnit {ZOMBIE, EAGLE, SHAMAN, UNIT_SPAWN, UNIT_NONE};
const quint8 predefinedUnitsNumber = 3;
const quint8 predefinedHeroesNumber = 4;

class Unit
{
public:
    Unit(GameMap* map, const UnitProperties& properties, quint8 x = 0, quint8 y = 0, quint8 teamNumber = NO_TEAM);
    const SpriteRef& getSpriteRef() const;
    const UnitProperties& getProperties() const;

    virtual void newTurn(); //updates the stats that depends on turns


    //moves
    QList<QPoint> pathTo(quint8 x, quint8 y);
    QList<QPoint> availableDestinations();
    virtual bool moveTo(quint8 x, quint8 y);    //returns true if the unit actually moved ; otherwise returns false
    bool isMovePossible();
    //actions
    bool canAttack(quint8 x, quint8 y);
    virtual void attack(Unit* target);  //calls canAttack() to throw an EX_FORBIDDEN_MOVE if the attack isn't possible
    QList<Unit*> canAttackSomeone() const;

    //animations
    bool animate();

    //setters
    void setGameMap(GameMap* map);
    void setTeamNumber(quint8 teamNumber);
    void setPos(quint8 x, quint8 y) {m_x = x; m_y = y; }
    void setRemainingMoves(quint8 q) { m_remainingMoves = q;}
    void setRemainingAction(bool b) { m_remainingAction = b; }
    void setOrdre(quint8 l) { ordre = l;}

    //getters
    quint8 getTeamNumber() const;
    const QString& getName() const;
    quint8 getX() const;
    quint8 getY() const;
    bool isIAControlled() const;

    quint8 getRemainingMoves() { return m_remainingMoves;}
    bool getRemainingAction() { return m_remainingAction; }
    QMap<QPoint, QList<QPoint> > getMap() { return m_pathsCache; }
    quint8 getOrdre() { return ordre; }
    void loseHealth(qint32 quantity);

protected:
    CharDirection stepDirection(const QPoint& target) const;

    GameMap* m_map;
    UnitProperties m_properties;
    quint8 ordre; // quand on enregistre, l'ordre des units dans lequel elles doivent jouer

    quint8 m_x; //x position in tiles
    quint8 m_y; //y position in tiles

    quint8 m_remainingMoves;
    bool m_remainingAction;

    QMap<QPoint, QList<QPoint> > m_pathsCache;  //Contains the already-calculated paths to minimize computation




    quint8 m_team;
    bool m_IAControlled;
    //private methods to move the Unit without doing any check
    void moveTowards(CharDirection direction);
    void moveTowards(const QPoint& stepTarget);

    QList<QPoint> m_animation;
    bool m_stepBegun;
    quint8 m_animationCount;
};

class UnitFactory {
    //Singleton that manages the predefined units (like monsters that can be put on maps)
public:
    static UnitFactory& getInstance();

    const UnitProperties& getUnitProperties(quint8 n) const;
    const UnitProperties& getHeroProperties(quint8 n) const;
    const SpriteRef& getSpawnSprite() const;

private:
    //The UnitFactory must be created using getInstance() as it's a singleton
    UnitFactory();
    UnitFactory(const UnitFactory&);
    UnitFactory& operator=(const UnitFactory&);

    UnitProperties m_units[predefinedUnitsNumber];
    UnitProperties m_playerUnits[predefinedHeroesNumber];
    SpriteRef m_spawnSprite;
};

#endif // UNIT_H
